/**
 * 利用多线程实现服务端
 * @author: sundx
 * @date: 2020-05-31 12:39:34
 * @Last Modified by: sundx
 * @Last Modified time: 2020-05-31 22:44:50
 */
#include "wrap.h"
#include <iostream>
#include <pthread.h>
#include <string.h>

void *connectDemo(void *i);
void checkPthreadError(const char *mes, int ret);

struct connectFdInfo { // 讲通信Fd 与 地址绑定；
  int connectFd;
  struct sockaddr_in clientAddr;
  int clientNum;
};

int main() {
  struct connectFdInfo connInfo[256]; // 结构体数组
  int i = 0; //用于在下文while循环中表示结构体数组的下标
  socklen_t clientAddrLen = sizeof(sockaddr_in);

  int listenFd = sdx::serverSocketBindListen();
  while (1) {
    connInfo[i].clientNum = i;
    connInfo[i].connectFd = sdx::Accept(
        listenFd, (sockaddr *)&connInfo[i].clientAddr, &clientAddrLen);
    pthread_t tid;
    // 创建子线程
    int ret = pthread_create(&tid, NULL, connectDemo, (void *)&connInfo[i]);
    checkPthreadError("pthread_create failed ", ret);
    ret = pthread_detach(tid); // 分离
    checkPthreadError("pthread_detach failed ", ret);
    ++i;
  }
  pthread_exit(nullptr);
  return 0;
}

void *connectDemo(void *i) { //子线程负责通信；
  struct connectFdInfo *connInfo = (connectFdInfo *)i;
  char inbuff[MESSAGE_SIZE];
  bzero((void *)inbuff, MESSAGE_SIZE); //初始化缓冲区；
  char clientAddrChar[INET_ADDRSTRLEN];
  while (1) {
    // 接受数据，字节数为recvBytes
    int recvBytes = recv(connInfo->connectFd, inbuff, MESSAGE_SIZE, 0);
    if (recvBytes == 0) { //判断是否是客户端关闭
      std::cout << "client " << connInfo->clientNum << " close the connect..."
                << std::endl;
      close(connInfo->connectFd);
      pthread_exit(nullptr);
    } else if (recvBytes == -1) {
      sdx::printError("recv failed");
    }

    std::cout << "receive " << recvBytes << " bytes message from "
              << inet_ntop(AF_INET, &connInfo->clientAddr.sin_addr,
                           clientAddrChar, INET_ADDRSTRLEN)
              << " and the port is remoteAddr "
              << ntohs(connInfo->clientAddr.sin_port) << ", and the message is "
              << inbuff << std::endl;

    for (int j = 0; j < recvBytes; ++j) { // 小写转大写
      inbuff[j] = toupper(inbuff[j]);
    }

    int ret = send(connInfo->connectFd, inbuff, recvBytes, 0); //发送
    if (ret == -1) {
      sdx::printError("recv failed");
    }
  }
  return nullptr;
}

// 检验线程函数报错，线程相关函数报错返回errno
void checkPthreadError(const char *mes, int ret) {
  if (ret != 0) {
    std::cout << mes << strerror(ret) << std::endl;
    exit(EXIT_FAILURE);
  }
}